#!/usr/bin/env python3

import ast
import json
from pathlib import Path
import pyte
import re
import shutil
import subprocess
import sys
import tempfile

def dconf_ls(path):
    output = subprocess.check_output(["dconf", "list", str(path) + "/"])
    return [path / line for line in output.decode().strip().split("\n")]

def dconf_read(path):
    output = subprocess.check_output(["dconf", "read", str(path)])
    return ast.literal_eval(output.decode())

def rgb_to_arr(rgb):
    return "".join(["{0:02x}".format(int(x)) for x in re.findall("[0-9]+", rgb)])

def get_terminal_conf():
    profiles = dconf_ls(Path("/org/gnome/terminal/legacy/profiles:/"))
    bgc = dconf_read(profiles[0] / "background-color")
    fgc = dconf_read(profiles[0] / "foreground-color")
    palette = dconf_read(profiles[0] / "palette")
    return (rgb_to_arr(bgc),
            rgb_to_arr(fgc),
            [rgb_to_arr(c) for c in palette])

def get_screen(filename):
    width = 0
    height = 0
    data = ""
    with open(filename, "r") as file:
        hdr = True
        for strline in file:
            line = json.loads(strline)
            if hdr:
                width = line['width']
                height = line['height']
                hdr = False
            else:
                data += line[2]
    # snip out weird escape code (??? what is this, some sort of window title?)
    data = re.sub(r'\x1b_.*?\x1b\\', "", data)
    screen = pyte.screens.HistoryScreen(width, height, history=9999999)
    stream = pyte.Stream(screen)
    stream.feed(data)
    lines = [l for l in screen.history.top]
    for i in range(len(screen.buffer)):
        lines.append(screen.buffer[i])
    return (width, height, lines)

def convert(filename, output):
    print("Generating latex")
    (bgc, fgc, palette) = get_terminal_conf()
    print(bgc, fgc, palette)
    (w, h, scr)= get_screen(filename)
    latexsrc = toTikz(scr, w, len(scr), bgc, fgc, palette)
    print("Compiling")
    with tempfile.TemporaryDirectory() as tmpdir:
        tmpdir = Path(tmpdir)
        with (tmpdir / "console.tex").open("w") as file:
            file.write(latexsrc)
        subprocess.check_call(["xelatex", "console.tex"], cwd=str(tmpdir))
        shutil.copyfile(tmpdir / "console.pdf", output)

# adapted from https://github.com/misc0110/asciicast2vector (MIT License)

def sanitizeLatexChar(c):
    if c in "&%$#_{}":
        return "\\" + c
    elif c == "~":
        return "\\textasciitilde"
    elif c == "^":
        return "\\textasciicircum"
    elif c == "\\":
        return "\\textbackslash"
    else:
        return c


def toTikz(lines, max_col, max_row, bgc, fgc, palette):
    pline = ""

    pline += tikzHeader(lines, bgc, fgc, palette)

    pline += "\\begin{tikzpicture}[yscale=-1]\\ttfamily\n"
    lspace = 1.8
    pline += "\\draw[fill=%s,draw=none] (-1em,-1em) rectangle +(%.4fem,%dem);\n" % ("defaultbg", (max_col + 4) / lspace, max_row + 2)
    for y in range(len(lines)):
        line = lines[y]
        for x in range(len(line)):
            char = line[x]
            if char.data == ' ' and ((char.reverse == False and char.bg == "default") or (char.reverse and char.fg == "default")):
                continue

            if char.reverse:
                fg = char.bg
                bg = char.fg
            else:
                fg = char.fg
                bg = char.bg

            if fg == "default":
                fg = "defaultfg"
            if bg == "default":
                bg = "defaultbg"

            content = sanitizeLatexChar(char.data)
            if char.bold:
                content = "\\textbf{" + content + "}"
            if char.italics:
                content = "\\textit{" + content + "}"

            pline += "\\draw[fill=%s,draw=none] (%.4fem,%dem) rectangle +(0.6em,1em) node[pos=.5, anchor=base, yshift=-0.5ex] {\\textcolor{%s}{%s}};\n" % (bg, x / lspace, y, fg, content)

    pline += "\\end{tikzpicture}"

    pline += tikzFooter()

    return pline

def color2tex(color, name=None):
    if name is None:
        name = color
    line = ""
    line += "\\definecolor{" + name + "}{RGB}{"
    line += str(int(color[:2], 16)) + ","
    line += str(int(color[2:4], 16)) + ","
    line += str(int(color[4:], 16)) + "}\n"
    return line

def tikzColors(lines, bgc, fgc, palette):
    line = ""
    line += color2tex(bgc, "defaultbg")
    line += color2tex(fgc, "defaultfg")
    for i,c in enumerate(["black", "red", "green", "brown", "blue", "magenta", "cyan", "white"]):
        line += color2tex(palette[i], c)
    colors = set(["default", "black", "red", "green", "brown", "blue", "magenta", "cyan", "white"])
    for y in range(len(lines)):
        _line = lines[y]
        for x in range(len(_line)):
            char = _line[x]
            if not char.bg in colors:
                colors.add(char.bg)
                line += color2tex(char.bg)
            if not char.fg in colors:
                colors.add(char.fg)
                line += color2tex(char.fg)

    return line

def tikzHeader(lines, bgc, fgc, palette):
    line = "\\documentclass[class=minimal,border=0pt]{standalone}\n"
    line += "\\usepackage[utf8]{inputenc}\n"
    line += "\\usepackage{tikz}\n"
    line += "\\usepackage[utf8]{inputenc}\n"
    line += "\\usepackage{fontspec}\n"
    line += "\\setmonofont[Ligatures=TeX]{Hack Regular}\n"
    # line += "\\usetikzlibrary{external}\n"
    # line += "\\tikzexternalize\n"
    line += "\\usepackage{xcolor}\n"
    line += "\\usepackage{amssymb}\n"
    line += "\\usepackage{pmboxdraw}\n"
    line += tikzColors(lines, bgc, fgc, palette)
    line += "\\begin{document}\n"
    return line

def tikzFooter():
    return "\\end{document}\n"

def main():
    if len(sys.argv) < 3:
        print("Usage: asciicast2latex <cast file> <pdf output>")
        sys.exit(-1)
    convert(sys.argv[1], sys.argv[2])

if __name__ == "__main__":
    main()
